package com.slorgs.business.utils;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class G711CodeUtil {
    private final static int SIGN_BIT = 0x80;
    private final static int QUANT_MASK = 0xf;
    private final static int SEG_SHIFT = 4;
    private final static int SEG_MASK = 0x70;
    private final static int BIAS = 0x84;
    private final static int CLIP = 8159;

    static short[] seg_end = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};

    static short search(short val, short[] table, short size) {

        for (short i = 0; i < size; i++) {
            if (val <= table[i]) {
                return i;
            }
        }
        return size;
    }

    static byte linear2alaw(short pcm_val) {
        short mask;
        short seg;
        char aval;
        if (pcm_val >= 0) {
            mask = 0xD5;
        } else {
            mask = 0x55;
            pcm_val = (short) (-pcm_val - 1);
            if (pcm_val < 0) {
                pcm_val = 32767;
            }
        }

        /* Convert the scaled magnitude to segment number. */
        seg = search(pcm_val, seg_end, (short) 8);

        /* Combine the sign, segment, and quantization bits. */

        if (seg >= 8)       /* out of range, return maximum value. */
            return (byte) (0x7F ^ mask);
        else {
            aval = (char) (seg << SEG_SHIFT);
            if (seg < 2)
                aval |= (pcm_val >> 4) & QUANT_MASK;
            else
                aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
            return (byte) (aval ^ mask);
        }
    }


    static byte linear2ulaw(short pcm_val)    /* 2's complement (16-bit range) */ {
        int mask;
        int seg;
        char uval;

        /* Get the sign and the magnitude of the value. */
        // pcm_val = (short) (pcm_val >> 2);
        if (pcm_val < 0) {
            pcm_val = (short) -pcm_val;
            mask = 0x7F;
        } else {
            mask = 0xFF;
        }
        if (pcm_val > CLIP) pcm_val = CLIP;        /* clip the magnitude 削波*/
        pcm_val += (BIAS >> 2);

        /* Convert the scaled magnitude to segment number. */
        seg = search(pcm_val, seg_end, (short) 8);

        /*
         * Combine the sign, segment, quantization bits;
         * and complement the code word.
         */
        if (seg >= 8)        /* out of range, return maximum value. */
            return (byte) (0x7F ^ mask);
        else {
            uval = (char) ((seg << 4) | ((pcm_val >> (seg + 3)) & 0xF));
            return (byte) (uval ^ mask);
        }
    }


    static short alaw2linear(byte a_val) {
        short t;
        short seg;

        a_val ^= 0x55;

        t = (short) ((a_val & QUANT_MASK) << 4);
        seg = (short) ((a_val & SEG_MASK) >> SEG_SHIFT);
        switch (seg) {
            case 0:
                t += 8;
                break;
            case 1:
                t += 0x108;
                break;
            default:
                t += 0x108;
                t <<= seg - 1;
        }
        return (a_val & SIGN_BIT) != 0 ? t : (short) -t;
    }

    /**
     * pcm 转 G711 a率
     */
    public static byte[] G711aEncoder(byte[] src, byte[] code, int size) {
        short[] pcm = bytesToShort(src);
        for (int i = 0; i < pcm.length; i++) {
            code[i] = linear2alaw(pcm[i]);
        }
//        log.info("数据码率:{}",code);
        return code;
    }

    private static short[] bytesToShort(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        short[] shorts = new short[bytes.length / 2];
        ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
        return shorts;
    }

    /**
     * G.711 转 PCM
     */
    public static byte[] G711aDecoder(byte[] src, byte[] code, int size) {
        short[] pcm = bytesToShort(src);
        for (int i = 0; i < size; i++) {
            pcm[i] = alaw2linear(code[i]);
        }
        return code;
    }

    /**
     * pcm 转 G711 u率
     */
    public static byte[] G711uEncoder(byte[] src, byte[] code, int size) {
        short[] pcm = bytesToShort(src);
        for (int i = 0; i < size; i++) {
            code[i] = linear2ulaw(pcm[i]);
        }
        return code;
    }
}

